using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;

namespace SpellCore
{
    public class Speller
    {
        private static string _spellCoreEngineDirectoryOverride;
        private static string _spellCoreName = "SpellCore";

        public static bool IsCorrect(string writingSystemId, string word)
        {
            if (!IsSpellCoreInstalled)
            {
                throw new InvalidOperationException("SpellCore is not installed");
            }
            return SpellChecker[writingSystemId].GetWordStatus(word) == CheckingResult.Good;
        }

        public static IEnumerable<SimilarityCheckResult> GetSuggestions(string writingSystemId, string word)
        {
            if (!IsSpellCoreInstalled)
            {
                throw new InvalidOperationException("SpellCore is not installed");
            }
            return SpellChecker[writingSystemId].GetSuggestions(word);
        }

        public static void AddGoodWord(string writingSystemId, string word)
        {
            if (!IsSpellCoreInstalled)
            {
                throw new InvalidOperationException("SpellCore is not installed");
            }
            SpellChecker[writingSystemId].AddGoodWord(word);
        }

        public static void AddBadWord(string writingSystemId, string word)
        {
            if (!IsSpellCoreInstalled)
            {
                throw new InvalidOperationException("SpellCore is not installed");
            }
            SpellChecker[writingSystemId].AddBadWord(word);
        }

        public static bool IsSpellCoreInstalled
        {
            get { return SpellChecker != null; }
        }

        public static bool CanSpellCheckWritingSystem(string writingSystemId)
        {
            ISpellCheckerRegistry checker = SpellChecker;
            if (checker == null)
            {
                return false;
            }

            return checker[writingSystemId].HasEngines;
        }

        public static ISpellCheckerRegistry SpellChecker
        {
            get
            {
                try
                {
                    string path = Path.Combine(PathToSpellCoreRoot, "SpellCore.dll");
                    Assembly root = Assembly.LoadFrom(path);
                    if (root == null)
                    {
                        return null;
                    }
                    Type type = root.GetType("SpellCore.SpellCheckerRegistry");
                    ISpellCheckerRegistry checker = type.InvokeMember("Checkers",
                                                                      BindingFlags.Public |
                                                                      BindingFlags.GetProperty | BindingFlags.Static,
                                                                      null,
                                                                      null,
                                                                      new object[] {}) as ISpellCheckerRegistry;

                    return checker;
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.Message);
                    return null;
                }
            }
        }

        /// <summary>
        /// Normally, c:\program files\common files\spellcore
        /// </summary>
        public static string PathToSpellCoreRoot
        {
            get
            {
                string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles);
                path = Path.Combine(path, SpellCoreName);
                if (!string.IsNullOrEmpty(_spellCoreEngineDirectoryOverride))
                {
                    path = _spellCoreEngineDirectoryOverride;
                }
                return path;
            }
        }

        public static string SpellCoreName
        {
            get { return _spellCoreName; }
            internal set { _spellCoreName = value; }
        }

        /// <summary>
        /// for automated tests
        /// </summary>
        internal static string SpellCoreEngineDirectoryOverride
        {
            set
            {
                _spellCoreEngineDirectoryOverride = value;
            }
        }

        internal static string UserDictionaryBaseDirectoryOverride
        {
            set
            {
                string path = Path.Combine(PathToSpellCoreRoot, "SpellCore.dll");
                Assembly root = Assembly.LoadFrom(path);
                if (root == null)
                {
                    throw new DllNotFoundException("Cannot load "+path);
                }
                Type type = root.GetType("SpellCore.HardCodedPathProviderFactory");
                type.InvokeMember("UserDictionaryBaseDirectory",
                                  BindingFlags.NonPublic |
                                  BindingFlags.SetProperty |
                                  BindingFlags.Static,
                                  null,
                                  null,
                                  new object[] {value});
            }
        }

        internal static string MainDictionaryBaseDirectoryOverride
        {
            set
            {
                string path = Path.Combine(PathToSpellCoreRoot, "SpellCore.dll");
                Assembly root = Assembly.LoadFrom(path);
                if (root == null)
                {
                    throw new DllNotFoundException("Cannot load " + path);
                }
                Type type = root.GetType("SpellCore.HardCodedPathProviderFactory");
                type.InvokeMember("SuppliedDictionaryBaseDirectory",
                                  BindingFlags.NonPublic |
                                  BindingFlags.SetProperty |
                                  BindingFlags.Static,
                                  null,
                                  null,
                                  new object[] { value });
            }
        }

    }
}